//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-10-14     ZhangYihua   first version
//
// Description  : (ar+j*ai)(br+j*bi) = (ar*br-ai*bi) + j*(ar*bi+br*ai)
//                for low area, pr = (ar+ai)*(br-bi) + ar*bi - br*ai;
//                              pi =  ar*bi + br*ai;
//################################################################################

module complex_mul #(
parameter           AW                      = 10,
parameter           BW                      = 12,

// the following parameters are calculated automatically
parameter           PW                      = AW+BW+1   // if ar,ai,br,bi is not all negative maximum, PW can be AW+BW
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input       signed  [AW-1:0]                ar,     // real      part of a
input       signed  [AW-1:0]                ai,     // imaginary part of a
input       signed  [BW-1:0]                br,     // real      part of b
input       signed  [BW-1:0]                bi,     // imaginary part of b

output      signed  [PW-1:0]                pr,     // real      part of p
output      signed  [PW-1:0]                pi      // imaginary part of p
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire        signed  [AW+1-1:0]              a_sum;
wire        signed  [BW+1-1:0]              b_diff;
wire        signed  [AW+BW+2-1:0]           p_ab_c;
wire        signed  [AW+BW-1:0]             p_ai_c;
wire        signed  [AW+BW-1:0]             p_bi_c;
reg         signed  [AW+BW+2-1:0]           p_ab;
reg         signed  [AW+BW-1:0]             p_ai;
reg         signed  [AW+BW-1:0]             p_bi;
wire        signed  [AW+BW+3-1:0]           pr_c;
wire        signed  [2-1:0]                 pr_nc;

//################################################################################
// main
//################################################################################

assign a_sum  = ar+ai;
assign b_diff = br-bi;

assign p_ab_c = a_sum*b_diff;
assign p_ai_c = ai*br;
assign p_bi_c = bi*ar;

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        p_ab <=`U_DLY {AW+BW+2{1'b0}};
        p_ai <=`U_DLY {AW+BW{1'b0}};
        p_bi <=`U_DLY {AW+BW{1'b0}};
    end else if (cke==1'b1) begin
        p_ab <=`U_DLY p_ab_c;
        p_ai <=`U_DLY p_ai_c;
        p_bi <=`U_DLY p_bi_c;
    end else
        ;
end

assign pi    = p_bi+p_ai;

assign pr_c  = p_bi-p_ai+p_ab;
assign pr    = pr_c[0+:AW+BW+1];
assign pr_nc = pr_c[AW+BW+1+:2];

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

// generally define CBB_DEBUG_ON only when debugging CBB
`ifdef CBB_DEBUG_ON
    
a_pr_high_nc: assert property (@(posedge clk) disable iff (!rst_n)
    ((pr_c[AW+BW+:3]==3'b000) || (pr_c[AW+BW+:3]==3'b111))
) else begin
    $error("pr_high is not same.");
end

`endif

// synopsys translate_on
`endif

endmodule
